package org.atlantis.grosbeak.lang;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 封装对IO的一些常用操作。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
public class IOs {

	private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

	/**
	 * Unix下的换行符。
	 */
	public static final String LINE_SEPARATOR_UNIX = "\n";

	/**
	 * Windows下的换行符。
	 */
	public static final String LINE_SEPARATOR_WINDOWS = "\r\n";

	/**
	 * 当前系统下的换行符。
	 */
	public static final String LINE_SEPARATOR;

	static {
		StringWriter buf = new StringWriter(4);
		PrintWriter out = new PrintWriter(buf);
		out.println();
		LINE_SEPARATOR = buf.toString();
	}

	/**
	 * 安全的关闭一个java.io.Reader。
	 * 
	 * @param reader
	 *            需要关闭的Reader。
	 */
	public static void closeQuietly(Reader reader) {
		try {
			if (reader != null)
				reader.close();
		} catch (Exception e) {
		}
	}

	/**
	 * 安全的关闭一个java.io.Writer。
	 * 
	 * @param writer
	 *            需要关闭的Writer。
	 */
	public static void closeQuietly(Writer writer) {
		try {
			if (writer != null)
				writer.close();
		} catch (Exception e) {
		}
	}

	/**
	 * 安全的关闭一个java.io.InputStream。
	 * 
	 * @param input
	 *            需要关闭的InputStream。
	 */
	public static void closeQuietly(InputStream input) {
		try {
			if (input != null)
				input.close();
		} catch (Exception e) {
		}
	}

	/**
	 * 安全的关闭一个java.io.OutputStream。
	 * 
	 * @param output
	 *            需要关闭的OutputStream。
	 */
	public static void closeQuietly(OutputStream output) {
		try {
			if (output != null)
				output.close();
		} catch (Exception e) {
		}
	}

	/**
	 * 从一个<code>InputStream</code>中复制字节到一个<code>OutputStream</code>中。
	 * 
	 * @param input
	 *            需要读取的<code>InputStream</code>。
	 * @param output
	 *            需要写入的<code>OutputStream</code>。
	 * @return 总过复制的字节数。
	 * @throws FileIOException
	 *             当复制字节出现问题时抛出。
	 */
	public static int copy(InputStream input, OutputStream output)
			throws FileIOException {
		long count = copyLarge(input, output);
		if (count > Integer.MAX_VALUE)
			return -1;
		return (int) count;
	}

	/**
	 * 从一个超过2GB的<code>InputStream</code>中复制字节到一个<code>OutputStream</code>中。
	 * 
	 * @param input
	 *            需要读取的<code>InputStream</code>。
	 * @param output
	 *            需要写入的<code>OutputStream</code>。
	 * @return 总过复制的字节数。
	 * 
	 * @throws FileIOException
	 *             当复制字节出现问题时抛出。
	 */
	public static long copyLarge(InputStream input, OutputStream output)
			throws FileIOException {
		byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
		long count = 0;
		int n = 0;
		try {
			while (-1 != (n = input.read(buffer))) {
				output.write(buffer, 0, n);
				count += n;
			}
		} catch (IOException e) {
			throw Exceptions.makeThrow(FileIOException.class, e,
					"Unable copy bytes from 'input' to 'output'.");
		}
		return count;
	}

	/**
	 * 从一个超过2GB的<code>Reader</code>中复制字节到一个<code>Writer</code>中。
	 * 
	 * @param reader
	 *            需要读取的<code>Reader</code>。
	 * @param writer
	 *            需要写入的<code>Writer</code>。
	 * @return 总过复制的字节数。
	 * 
	 * @throws FileIOException
	 *             当复制字节出现问题时抛出。
	 */
	public static long copyLarge(Reader reader, Writer writer)
			throws FileIOException {
		char[] buffer = new char[DEFAULT_BUFFER_SIZE];
		long count = 0;
		int n = 0;
		try {
			while (-1 != (n = reader.read(buffer))) {
				writer.write(buffer, 0, n);
				count += n;
			}
		} catch (IOException e) {
			throw Exceptions.makeThrow(FileIOException.class, e,
					"Unable copy bytes from 'reader' to 'writer'.");
		}
		return count;
	}

	/**
	 * 从一个<code>Reader</code>中复制字节到一个<code>Writer</code>中。
	 * 
	 * @param reader
	 *            需要读取的<code>Reader</code>。
	 * @param writer
	 *            需要写入的<code>Writer</code>。
	 * @return 总过复制的字节数。
	 * 
	 * @throws FileIOException
	 *             当复制字节出现问题时抛出。
	 */
	public static int copy(Reader reader, Writer writer) throws FileIOException {
		long count = copyLarge(reader, writer);
		if (count > Integer.MAX_VALUE)
			return -1;
		return (int) count;
	}

	/**
	 * 从一个<code>InputStream</code>中复制字节到一个<code>Writer</code>中。
	 * 
	 * @param input
	 * @param writer
	 * @throws FileIOException
	 */
	public static void copy(InputStream input, Writer writer)
			throws FileIOException {
		InputStreamReader reader = new InputStreamReader(input);
		copy(reader, writer);
	}

	/**
	 * 从一个<code>InputStream</code>中按照指定编码<code>encoding</code>复制字节到一个
	 * <code>Writer</code>中。
	 * 
	 * @param input
	 * @param writer
	 * @param encoding
	 * @throws FileIOException
	 */
	public static void copy(InputStream input, Writer writer, String encoding)
			throws FileIOException {
		if (encoding == null) {
			copy(input, writer);
		} else {
			try {
				InputStreamReader reader = new InputStreamReader(input,
						encoding);
				copy(reader, writer);
			} catch (UnsupportedEncodingException e) {
				throw Exceptions.makeThrow(FileIOException.class, e,
						"Unsupport the encoding '%s'.", encoding);
			}
		}
	}

	/**
	 * 从一个<code>Reader</code>中复制字节到一个<code>OutputStream</code>中。
	 * 
	 * @param reader
	 * @param output
	 * @throws FileIOException
	 */
	public static void copy(Reader reader, OutputStream output)
			throws FileIOException {
		OutputStreamWriter writer = new OutputStreamWriter(output);
		copy(reader, writer);
		try {
			writer.flush();
		} catch (IOException e) {
		}
	}

	/**
	 * 从一个<code>Reader</code>中按照指定编码<code>encoding</code>复制字节到一个
	 * <code>OutputStream</code>中。
	 * 
	 * @param reader
	 * @param output
	 * @param encoding
	 * @throws FileIOException
	 */
	public static void copy(Reader reader, OutputStream output, String encoding)
			throws FileIOException {
		if (encoding == null) {
			copy(reader, output);
		} else {
			try {
				OutputStreamWriter writer = new OutputStreamWriter(output,
						encoding);
				copy(reader, writer);
				writer.flush();
			} catch (UnsupportedEncodingException e) {
				throw Exceptions.makeThrow(FileIOException.class, e,
						"Unsupport the encoding '%s'.", encoding);
			} catch (IOException e) {
			}
		}
	}

	/**
	 * 把InputStream中的内容转换成一个字符串。
	 * 
	 * @param input
	 *            获取内容的InputStream。
	 * @return 字符串。
	 * @throws FileIOException
	 */
	public static String toString(InputStream input) throws FileIOException {
		StringWriter sw = new StringWriter();
		copy(input, sw);
		return sw.toString();
	}

	/**
	 * 把InputStream中的内容按照指定编码转换成一个字符串。
	 * 
	 * @param input
	 *            获取内容的InputStream。
	 * @param encoding
	 *            指定编码。
	 * @return 字符串。
	 * @throws FileIOException
	 */
	public static String toString(InputStream input, String encoding)
			throws FileIOException {
		StringWriter sw = new StringWriter();
		copy(input, sw, encoding);
		return sw.toString();
	}

	/**
	 * 把Reader中的内容转换成一个字符串。
	 * 
	 * @param reader
	 *            获取内容的Reader。
	 * @return 字符串。
	 * @throws FileIOException
	 */
	public static String toString(Reader reader) throws FileIOException {
		StringWriter sw = new StringWriter();
		copy(reader, sw);
		return sw.toString();
	}

	/**
	 * 一行一行的从Reader中获取内容，然后把每一行内容保存到一个List中。
	 * 
	 * @param reader
	 *            获取内容的Reader。
	 * @return 包含每一行内容的List。
	 * @throws FileIOException
	 */
	public static List<String> readLines(Reader reader) throws FileIOException {
		BufferedReader br = new BufferedReader(reader);
		List<String> list = new ArrayList<String>();
		try {
			String line = br.readLine();
			while (line != null) {
				list.add(line);
				line = br.readLine();
			}
		} catch (IOException e) {
			throw Exceptions.makeThrow(FileIOException.class, e,
					"Unable to read line from 'reader'.");
		}
		return list;
	}

	/**
	 * 一行一行的从InputStream中获取内容，然后把每一行内容保存到一个List中。
	 * 
	 * @param input
	 *            获取内容的InputStream。
	 * @return 包含每一行内容的List。
	 * @throws FileIOException
	 */
	public static List<String> readLines(InputStream input)
			throws FileIOException {
		InputStreamReader reader = new InputStreamReader(input);
		return readLines(reader);
	}

	/**
	 * 一行一行的从InputStream中按照指定编码获取内容，然后把每一行内容保存到一个List中。
	 * 
	 * @param input
	 *            获取内容的InputStream。
	 * @param encoding
	 *            指定编码。
	 * @return 包含每一行内容的List。
	 * @throws FileIOException
	 */
	public static List<String> readLines(InputStream input, String encoding)
			throws FileIOException {
		if (encoding == null) {
			return readLines(input);
		} else {
			try {
				InputStreamReader reader = new InputStreamReader(input,
						encoding);
				return readLines(reader);
			} catch (UnsupportedEncodingException e) {
				throw Exceptions.makeThrow(FileIOException.class, e,
						"Unsupport the encoding '%s'.", encoding);
			}
		}
	}

	/**
	 * 把lines中的每一行内容都写入到一个OutputStream中，每行写完后加上一个换行符。
	 * 
	 * @param lines
	 *            要写入的内容。
	 * @param lineEnding
	 *            换行符。
	 * @param output
	 * @throws FileIOException
	 */
	public static void writeLines(Collection<String> lines, String lineEnding,
			OutputStream output) throws FileIOException {
		if (lines == null)
			return;
		if (lineEnding == null)
			lineEnding = LINE_SEPARATOR;
		try {
			for (String line : lines) {
				if (line != null)
					output.write(line.getBytes());
				output.write(lineEnding.getBytes());
			}
		} catch (IOException e) {
			throw Exceptions.makeThrow(FileIOException.class, e,
					"Unable write line to OutputStream.");
		}
	}

	/**
	 * 把lines中的每一样内容按照指定编码写入到一个OutputStream中，每行写完后加上一个换行符。
	 * 
	 * @param lines
	 *            要写入的内容。
	 * @param lineEnding
	 *            换行符。
	 * @param output
	 * @param encoding
	 *            指定编码。
	 * @throws FileIOException
	 */
	public static void writeLines(Collection<String> lines, String lineEnding,
			OutputStream output, String encoding) throws FileIOException {
		if (encoding == null) {
			writeLines(lines, lineEnding, output);
		} else {
			if (lines == null)
				return;
			if (lineEnding == null)
				lineEnding = LINE_SEPARATOR;
			try {
				for (String line : lines) {
					if (line != null)
						output.write(line.getBytes(encoding));
					output.write(lineEnding.getBytes(encoding));
				}
			} catch (IOException e) {
				throw Exceptions.makeThrow(FileIOException.class, e,
						"Unable write line to OutputStream.");
			}
		}
	}

	/**
	 * 把lines中的每一行内容都写入到一个Writer中，每行写完后加上一个换行符。
	 * 
	 * @param lines
	 *            要写入的内容。
	 * @param lineEnding
	 *            换行符。
	 * @param writer
	 * @throws FileIOException
	 */
	public static void writeLines(Collection<String> lines, String lineEnding,
			Writer writer) throws FileIOException {
		if (lines == null)
			return;
		if (lineEnding == null)
			lineEnding = LINE_SEPARATOR;
		try {
			for (String line : lines) {
				if (line != null)
					writer.write(line);
				writer.write(lineEnding);
			}
		} catch (IOException e) {
			throw Exceptions.makeThrow(FileIOException.class, e,
					"Unable to write 'data' to 'writer'");
		}
	}

	/**
	 * 把指定内容'data'写入到一个OutputStream中。
	 * 
	 * @param data
	 *            需要写入的内容。
	 * @param output
	 *            内容写入的OutputStream。
	 * @throws FileIOException
	 */
	public static void write(byte[] data, OutputStream output)
			throws FileIOException {
		if (data != null)
			try {
				output.write(data);
			} catch (IOException e) {
				throw Exceptions.makeThrow(FileIOException.class, e,
						"Unable to write 'data' to 'output'");
			}
	}

	/**
	 * 把指定内容'data'写入到一个Writer中。
	 * 
	 * @param data
	 *            需要写入的内容。
	 * @param writer
	 *            内容写入的Writer。
	 * @throws FileIOException
	 */
	public static void write(byte[] data, Writer writer) throws FileIOException {
		if (data != null)
			try {
				writer.write(new String(data));
			} catch (IOException e) {
				throw Exceptions.makeThrow(FileIOException.class, e,
						"Unable to write 'data' to 'output'");
			}
	}

	/**
	 * 把指定内容'data'按照指定编码'encoding'写入到一个Writer中。
	 * 
	 * @param data
	 *            需要写入的内容。
	 * @param writer
	 *            内容写入的Writer。
	 * @param encoding
	 *            指定编码。
	 * @throws FileIOException
	 */
	public static void write(byte[] data, Writer writer, String encoding)
			throws FileIOException {
		if (data != null) {
			if (encoding == null)
				write(data, writer);
			else
				try {
					writer.write(new String(data, encoding));
				} catch (IOException e) {
					throw Exceptions.makeThrow(FileIOException.class, e,
							"Unable to write 'data' to 'output'");
				}
		}
	}

	/**
	 * 把指定内容'data'写入到一个OutputStream中。
	 * 
	 * @param data
	 * @param output
	 * @throws FileIOException
	 */
	public static void write(String data, OutputStream output)
			throws FileIOException {
		if (data != null) {
			try {
				output.write(data.getBytes());
			} catch (IOException e) {
				throw Exceptions.makeThrow(FileIOException.class, e,
						"Unable to write 'data' to 'output'");
			}
		}
	}

	/**
	 * 把指定内容'data'按照指定编码'encoding'写入到一个OutputStream中。
	 * 
	 * @param data
	 * @param output
	 * @param encoding
	 * @throws FileIOException
	 */
	public static void write(String data, OutputStream output, String encoding)
			throws FileIOException {
		if (data != null) {
			if (encoding == null)
				write(data, output);
			else
				try {
					output.write(data.getBytes(encoding));
				} catch (UnsupportedEncodingException e) {
					throw Exceptions.makeThrow(FileIOException.class, e,
							"Unsupport the encoding '%s'.", encoding);
				} catch (IOException e) {
					throw Exceptions.makeThrow(FileIOException.class, e,
							"Unable to write 'data' to 'output'");
				}
		}
	}
}
